package com.common.util;

import com.google.protobuf.GeneratedMessageV3;
import com.google.protobuf.Message;
import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by sun on 2018/1/12.
 */

public class HttpUtils {
    //默认超时时间
    private static int timeOut = 3 * 10000;
    //HTTP请求提交方式
    private static final String METHOD_POST = "POST";
    private static final String METHOD_GET = "GET";
    //默认编码
    private static final String CHARSET_DEFAULT = "UTF-8";
    //分隔符
    private static final String BOUNDARY = "***---****-*";
    //content-type multipart数据格式，文件流提交
    private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data;boundary=" + BOUNDARY;
    //content-type application 普通数据提交
    private static final String CONTENT_TYPE_APPLICATION = "application/x-www-form-urlencoded";
    //连接符
    private static final String HYPHENS = "--";
    //结束换行符
    private static final String END = "\r\n";
    // 定义缓冲区大小
    private static final int BUFFER_SIZE = 1024;
    //文件上传默认name
    private static final String FILE_NAME_DEFAULT = "file";

    private static HttpURLConnection createConnection(String actionUrl, String method) throws IOException {
        URL url = new URL(actionUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoInput(true);
        conn.setConnectTimeout(timeOut);
        conn.setRequestMethod(method);
        conn.setUseCaches(false);
        if (METHOD_POST.equals(method)) {
            conn.setDoOutput(true);
        } else if (METHOD_GET.equals(method)) {
            conn.setDoOutput(false);
        }
        return conn;
    }

    private static HttpURLConnection createConnection(String actionUrl, String method, boolean useCaches) throws IOException {
        HttpURLConnection conn = createConnection(actionUrl, method);
        conn.setUseCaches(useCaches);
        return conn;
    }

    /**
     * 发送post请求
     *
     * @param actionUrl 请求目标URL
     * @param params    参数列表
     * @return 请求成功return 服务器返回的结果，失败返回null<br>
     * 注意：服务器返回的字符串"null" 与 失败返回的null的区别
     * @throws IOException
     */
    public static String sendPost(String actionUrl, Map<String, String> params) throws IOException {
        HttpURLConnection conn =null;
        DataOutputStream out=null;
        String result = null;
        try {
            //创建Http连接
            conn = createConnection(actionUrl, METHOD_POST);
            //设置请求头
            setHeaders(conn, false);
            //打包请求体数据
            byte[] data = packData(params);
            //写入数据建立连接
            out = new DataOutputStream(conn.getOutputStream());
            out.write(data);
            out.flush();
            conn.connect();
            //处理响应
            result = dealResponse(conn);
        }catch (Exception e){
            throw e;
        }finally {
            colseResource(conn,out);
        }
        return result;
    }

    /**
     * 释放资源
     * @param conn
     * @param out
     */
    public static void colseResource(HttpURLConnection conn,OutputStream out){
        if (out!=null){
            try {
                out.close();
            } catch (IOException e) {

            }finally {
                out=null;
            }
        }
        try {
            if (conn!=null){
                conn.disconnect();
            }
        }catch (Exception e) {

        }finally {
            conn=null;
        }
    }
    /**
     * 基于HTTP上传文件
     * @param actionUrl 目标URL
     * @param fileMap 要上传的文件列表，key为name，value为文件路劲
     * @param name 要上传的文件数组的name属性
     * @param pathList 要上传的文件数组的路劲list
     * @param params 附带的参数
     * @return 请求成功return 服务器返回的结果，失败返回null<br>
     * @throws IOException
     */
    public static String uploadFiles(String actionUrl, Map<String, String> fileMap, String name, List<String> pathList, Map<String, String> params) throws IOException {
        HttpURLConnection conn =null;
        DataOutputStream out=null;
        String result = null;
        try {
            //创建Http连接
            conn = createConnection(actionUrl, METHOD_POST);
            //设置请求头
            setHeaders(conn, true);
            //打包请求体
            byte[] data = packData(params, pathList, name, fileMap);
            //写入数据建立连接
            out = new DataOutputStream(conn.getOutputStream());
            out.write(data);
            out.flush();
            conn.connect();
            //处理响应
            result = dealResponse(conn);
        } catch (Exception e){
            throw e;
        }finally {
            colseResource(conn,out);
        }
        return result;
    }

    /**
     * 基于HTTP上传文件<br>
     *     文件数组的name默认为"file"
     * @param actionUrl 目标URL
     * @param pathList 要上传的文件数组的路劲list
     * @return 请求成功return 服务器返回的结果，失败返回null<br>
     * @throws IOException
     */
    public static String uploadFiles(String actionUrl,List<String> pathList) throws IOException {
        return uploadFiles(actionUrl,null,FILE_NAME_DEFAULT,pathList,null);
    }
    /**
     * 基于HTTP上传文件<br>
     *     文件数组的name默认为"file"
     * @param actionUrl 目标URL
     * @param pathList 要上传的文件数组的路劲list
     * @param params 要同时上传的参数
     * @return 请求成功return 服务器返回的结果，失败返回null<br>
     * @throws IOException
     */
    public static String uploadFiles(String actionUrl,List<String> pathList,Map params) throws IOException {
        return uploadFiles(actionUrl,null,FILE_NAME_DEFAULT,pathList,params);
    }


    /**
     * 基于HTTP上传文件
     * @param actionUrl 目标URL
     * @param name  要上传的文件数组的name
     * @param pathList  要上传的文件数组的路劲list
     * @return 请求成功return 服务器返回的结果，失败返回null<br>
     * @throws IOException
     */
    public static String uploadFiles(String actionUrl,String name, List<String> pathList) throws IOException {
        return uploadFiles(actionUrl,null,name,pathList,null);
    }

    /**
     *
     * @param conn
     * @return
     * @throws IOException
     */
    private static String dealResponse(HttpURLConnection conn) throws IOException {
        int code = conn.getResponseCode();
        if (HttpURLConnection.HTTP_OK == code) {
            return convertStream(conn.getInputStream());
        }
        return null;
    }


    //TODO 流处理方法,可以独立
    /**
     * 将流转换成为字符串
     *
     * @param inputStream
     * @return
     */
    private static String convertStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(data)) != -1) {
            byteArrayOutputStream.write(data, 0, len);
        }
        return new String(byteArrayOutputStream.toByteArray());
    }

    /**
     * @return
     * @throws IOException
     */
    public static String get(String path) throws IOException {
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            //GET请求直接在链接后面拼上请求参数
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            //Get请求不需要DoOutPut
            conn.setDoOutput(false);
            conn.setDoInput(true);
            //设置连接超时时间和读取超时时间
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            //连接服务器
            conn.connect();
            conn.getResponseCode();
            InputStream inputStream = conn.getInputStream();
            // 取得输入流，并使用Reader读取
            in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result.toString();
    }

    public static int MNGet(String path){
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            //GET请求直接在链接后面拼上请求参数
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            //Get请求不需要DoOutPut
            conn.setDoOutput(false);
            conn.setDoInput(true);
            //设置连接超时时间和读取超时时间
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36");
            //连接服务器
            conn.connect();
//            InputStream inputStream = conn.getInputStream();
//            // 循环取出流中的数据
//            byte[] by = new byte[1024];
//            int len;
//            while ((len = inputStream.read(by)) > 0){
//
//            }
            InputStream inputStream = conn.getInputStream();
            // 取得输入流，并使用Reader读取
            in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            inputStream.close();
            return conn.getResponseCode();
        }catch (Exception e){
            return 0;
        }
    }

    /**
     *
     * @param conn
     * @param hasFile
     */
    private static void setHeaders(HttpURLConnection conn, boolean hasFile) {
        conn.setRequestProperty("Charset", CHARSET_DEFAULT);
        if (hasFile) {
            conn.setRequestProperty("Content-Type", CONTENT_TYPE_MULTIPART);
        } else {
            conn.setRequestProperty("Content-Type", CONTENT_TYPE_APPLICATION);
        }
    }

    /**
     * 自定义请求头
     *
     * @param conn
     * @param hasFile
     * @param headers
     */
    private static void setHeaders(HttpURLConnection conn, boolean hasFile, Map<String, String> headers) {
        setHeaders(conn, hasFile);
        Iterator<String> iterator = headers.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = headers.get(key);
            conn.setRequestProperty(key, value);
        }
    }


    /**
     * Multipart 数据封装
     * @param params
     * @param pathList
     * @param name
     * @param fileMap
     * @return
     * @throws IOException
     */
    private static byte[] packData(Map<String, String> params, List<String> pathList, String name, Map<String, String> fileMap) throws IOException {
        //
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        //参数转换成Multipart字节
        if (params != null && !params.isEmpty()) {
            writeParmsByMultipart(bytes, params);
        }
        //将文件数组数据转换成字节
        if (name != null && pathList != null && pathList.size() > 0) {
            writeFileArrayByMultipart(bytes, name, pathList);
        }
        //将文件列表文件写入流
        if (fileMap != null && !fileMap.isEmpty()) {
            writeFilesByMultipart(bytes, fileMap);
        }
        bytes.write((HYPHENS + BOUNDARY + HYPHENS + END).getBytes(CHARSET_DEFAULT));
        return bytes.toByteArray();
    }

    /**
     * 将一个文件数组数据写到流中
     *
     * @param bytes
     * @param name
     * @param pathList
     * @throws IOException
     */
    private static void writeFileArrayByMultipart(ByteArrayOutputStream bytes, String name, List<String> pathList) throws IOException {
        for (int i = 0; i < pathList.size(); i++) {
            //写入文件信息
            File file = new File(pathList.get(i));
            writeFileInfoByMultipart(bytes, name, file);
        }
    }

    /**
     * 将一个文件列表的文件写入到流
     *
     * @param bytes
     * @param fileMap
     * @throws IOException
     */
    private static void writeFilesByMultipart(ByteArrayOutputStream bytes, Map<String, String> fileMap) throws IOException {
        Iterator<String> iterator = fileMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            File file = new File(fileMap.get(key));
            writeFileInfoByMultipart(bytes, key, file);
        }
    }

    //TODO 可独立

    /**
     * 读取文件内容
     *
     * @param bytes
     * @param file
     * @return
     * @throws IOException
     */
    private static void readFile(ByteArrayOutputStream bytes, File file) throws IOException {
        // 实例化文件输入流对象，将文件路径传入，用于将磁盘上的文件读入到内存中
        FileInputStream fileInputStream = new FileInputStream(file);
        // 定义字节数组对象，用来读取缓冲区数据
        byte[] buffer = new byte[BUFFER_SIZE];
        // 定义一个整形变量，用来存放当前读取到的文件长度
        int length;
        // 循环从文件输出流中读取1024字节的数据，将每次读取的长度赋值给length变量，直到文件读取完毕，值为-1结束循环
        while ((length = fileInputStream.read(buffer)) != -1) {
            // 向数据输出流中写出数据
            bytes.write(buffer, 0, length);
        }
    }

    /**
     * 根据Multipart规则拼接参数
     *
     * @param bytes
     * @param params 参数列表
     * @return
     */
    private static void writeParmsByMultipart(ByteArrayOutputStream bytes, Map<String, String> params) throws IOException {
        Iterator<String> iterator = params.keySet().iterator();
        StringBuilder sb = new StringBuilder();
        while (iterator.hasNext()) {
            String key = iterator.next();
            sb.append(HYPHENS + BOUNDARY + END);
            sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + END);
            sb.append(END);
            sb.append(params.get(key));
            sb.append(END);
        }
        bytes.write(sb.toString().getBytes(CHARSET_DEFAULT));
    }

    /**
     * 封装一个文件的数据包装文件相关信息以及文件流信息。
     *
     * @param name
     * @param file
     * @return
     * @throws IOException
     */
    private static void writeFileInfoByMultipart(ByteArrayOutputStream bytes, String name, File file) throws IOException {
        //写入文件相关信息
        StringBuilder sb = new StringBuilder();
        sb.append(HYPHENS + BOUNDARY + END);
        sb.append("Content-Disposition:form-data;name=" + name + ";filename=" + file.getName() + END);
        sb.append(END);
        bytes.write(sb.toString().getBytes(CHARSET_DEFAULT));
        //写入文件的内容流
        readFile(bytes, file);
        bytes.write(END.getBytes(CHARSET_DEFAULT));
    }

    /**
     * @param params
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] packData(Map<String, String> params) throws UnsupportedEncodingException {
        StringBuffer stringBuffer = new StringBuffer();
        if(params != null && !params.isEmpty()) {
            //存储封装好的请求体信息
            for (Map.Entry<String, String> entry : params.entrySet()) {
                stringBuffer.append(entry.getKey())
                        .append("=")
                        .append(entry.getValue())
                        .append("&");
            }
            //删除最后的一个"&"
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        }
        return stringBuffer.toString().getBytes(CHARSET_DEFAULT);
    }
    //TODO 自定义请求头的方法，

    /**
     * 根据url下载文件,保存的文件名是url末尾的文件名。
     * @param urlPath
     * @param downloadDir
     * @return
     */
    public static String downloadFile(String urlPath, String downloadDir) {
        File file = null;
        String fileFullName = null;
        try {
            // 统一资源
            urlPath=urlPath.replace("https","http");
            URL url = new URL(urlPath);
            // 连接类的父类，抽象类
            URLConnection urlConnection = url.openConnection();
            // http的连接类
            HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
            // 设定请求的方法，默认是GET
            httpURLConnection.setRequestMethod("GET");
            // 设置字符编码
            httpURLConnection.setRequestProperty("Charset", "UTF-8");
            // 打开到此 URL 引用的资源的通信链接（如果尚未建立这样的连接）。
            httpURLConnection.connect();
            // 文件大小
            int fileLength = httpURLConnection.getContentLength();
            // 文件名
            String filePathUrl = httpURLConnection.getURL().getFile();
            fileFullName = filePathUrl.substring(filePathUrl.lastIndexOf("/") + 1);
            BufferedInputStream bin = new BufferedInputStream(httpURLConnection.getInputStream());

            String path = downloadDir + File.separatorChar + fileFullName;
            file = new File(path);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            OutputStream out = new FileOutputStream(file);
            int size = 0;
            int len = 0;
            byte[] buf = new byte[1024];
            while ((size = bin.read(buf)) != -1) {
                len += size;
                out.write(buf, 0, size);
            }
            bin.close();
            out.close();
            BufferedImage bi = ImageIO.read(file);
            if(bi != null){
                fileFullName = compressOne(path,fileFullName);
            }
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            return fileFullName;
        }
    }

//    public static void main(String[] args){
//        String aaa = downloadFile("http://file2.pingxiaobao.com/prd/1910/21/aa5d2ce0f4191206b366c95f7d7e8bcb.jpeg","D:\\");
//        System.out.println(aaa);
//    }

    public static byte[] sendProtobufByPost(String url,byte[] data) throws IOException {
        HttpURLConnection connection = createConnection(url, METHOD_POST);
        connection.setDoOutput(true);
        connection.setRequestProperty("Accept","application/x-protobuf");
        connection.setRequestProperty("Content-Type", "application/x-protobuf");
        connection.getOutputStream().write(data);

        connection.connect();
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        InputStream in = connection.getInputStream();
        byte[] temp = new byte[1024];
        int rc = 0;
        while ((rc = in.read(temp, 0, temp.length)) > 0) {
            bo.write(temp, 0, rc);
        }
        return bo.toByteArray();
    }

    public static String compressOne(String path,String fileFullName){
        BufferedImage bufferedImage;
        try {
            String pathd = "";
            int i = path.lastIndexOf(".");
            if(i>0){
                pathd = path.substring(i+1);
            }
            if(pathd.equals("png")){
                pathd = path.substring(0,i)+".jpg";
                fileFullName = fileFullName.substring(0,fileFullName.lastIndexOf("."))+".jpg";
                bufferedImage = ImageIO.read(new File(path));
                BufferedImage newBufferedImage = new BufferedImage(bufferedImage.getWidth(),
                      bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
                newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0, Color.WHITE, null);
                ImageIO.write(newBufferedImage, "jpg", new File(pathd));
                File f = new File(path);
                f.delete();
            }else{
                pathd = path;
            }
            Thumbnails.of(pathd)
                  .scale(1)
                  .outputQuality(0.5)
                  .toFile(pathd);
            return fileFullName;
        }catch (Exception e ){
            e.printStackTrace();
            return fileFullName;
        }
    }

    /**
     * @return
     * @throws IOException
     */
    public static String getUnicom(String path,String apiKey) throws IOException {
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            //GET请求直接在链接后面拼上请求参数
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            //Get请求不需要DoOutPut
            conn.setDoOutput(false);
            conn.setDoInput(true);
            //设置连接超时时间和读取超时时间
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Authorization","Basic "+apiKey);
            //连接服务器
            conn.connect();
            conn.getResponseCode();
            InputStream inputStream = conn.getInputStream();
            // 取得输入流，并使用Reader读取
            in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
//            e.printStackTrace();
        }
        //关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
//                ex.printStackTrace();
            }
        }
        return result.toString();
    }
}
